package ru.niir.goodfil.db;

import org.apache.commons.lang.StringUtils;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotEmpty;
import ru.niir.goodfil.db.dic.ClientState;
import ru.niir.goodfil.db.dic.DeliveryType;

import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static org.apache.commons.lang.StringUtils.isBlank;
import static ru.niir.goodfil.utils.Utils.s;

/**
 * Клиент
 */
@Entity
@Table(name = "`Clients`")
public class Client implements Unique,Serializable {
    /**
     * Идентификатор
     */
    @Id
    @GeneratedValue
    @Column(name = "`Id`", nullable = false)
    private long id = -1;

//    /**
//     * Идентификатор клиентской группы, к которой относится клиент
//     */
//    @Column(name = "`IdClientGroup`", nullable = false)
//    private long clientGroupId;

    /**
     * Идентификатор менеджера, который работает с клиентом
     */
    @Column(name = "`IdManager`", nullable = true)
    private Long managerId;

    /**
     * Наименование компании (основное наименование клиента)
     */
    @NotEmpty(message = "Поле Название компании должно быть заполнено")
    @Length(max = 300, message = "Длина поля Название компании не должна превышать 300 символов")
    @Column(name = "`CompanyName`", nullable = false)
    private String companyName = "";

    /**
     * Юридическое наименование компании
     */
    @Length(max = 300, message = "Длина поля Юридическое название компании не должна превышать 300 символов")
    @Column(name = "`LowName`", nullable = false)
    private String lowName = "";

    /**
     * Тип компании. Обычно это склад/магазин/сервис
     */
    @Length(max = 300, message = "Длина поля Вид деятельностьи не должна превышать 300 символов")
    @Column(name = "`CompanyType`", nullable = false)
    private String companyType = "";

    /**
     * Специализация компании. Перечень торговых марок.
     */
    @Length(max = 60, message = "Длина поля Специализация не должна превышать 60 символов")
    @Column(name = "`Speciality`", nullable = false)
    private String speciality = "";

    /**
     * Интернет сай компании
     */
    @Length(max = 50, message = "Длина поля Сайт не должна превышать 50 символов")
    @Column(name = "`Site`", nullable = false)
    private String site = "";

    /**
     * Флаг показывает, что клиент активен
     */
    @Column(name = "`IsActive`", nullable = false)
    private boolean active = true;

    /**
     * Флаг контроля суммы закупок за предыдущий месяц
     */
    @Column(name = "`AllowControlSum`", nullable = false)
    private boolean allowControlSum = true;

    /**
     * Флаг допуска оформления заказов без согласования с менеджером
     */
    @Column(name = "`AllowQuickOrder`", nullable = false)
    private boolean allowQuickOrder = false;

    /**
     * Номер клиента в 1С
     */
    @Column(name = "`1CClientNum`", nullable = false)
    private String c1ClientNum = "";


    //
    //  Адрес
    //

    /**
     * Фактический адрес
     */
    @Column(name = "`AddressFact`", nullable = false)
    private String addressFact = "";

    /**
     * Юридический адрес
     */
    @Column(name = "`AddressLaw`", nullable = false)
    private String addressLaw = "";

    /**
     * Адрес доставки
     */
    @Column(name = "`AddressDlv`", nullable = false)
    private String addressDlv = "";


    //
    //  Контактное лицо
    //

    @Column(name = "`ContactFio`", nullable = false)
    @Length(max = 122, message = "Длина поля ФИО менеджера не должна превышать 122 символов")
//    @NotEmpty(message = "Поле ФИО менеджера должно быть заполнено")
    private String contactFio = "";

    @Column(name = "`ContactPhoneMob`", nullable = false)
    @Length(max = 100, message = "Длина поля Мобильный телефон менеджера не должна превышать 100 символов")
//    @NotEmpty(message = "Поле Мобильный телефон менеджера должно быть заполнено")
    private String contactPhoneMob = "";

    @Column(name = "`ContactPhoneWork`", nullable = false)
    @Length(max = 100, message = "Длина поля Рабочий телефон менеджера не должна превышать 100 символов")
//    @NotEmpty(message = "Поле Рабочий телефон менеджера должно быть заполнено")
    private String contactPhoneWork = "";

    @Column(name = "`ContactEmail`", nullable = false)
    @Length(max = 500, message = "Длина поля EMail менеджера не должна превышать 500 символов")
    @NotEmpty(message = "Поле EMail менеджера должно быть заполнено")
    private String contactEmail = "";


    //
    //  Руководитель
    //

    @Column(name = "`BossFio`", nullable = false)
    @Length(max = 122, message = "Длина поля ФИО руководителя не должна превышать 122 символов")
    private String bossFio = "";

    @Column(name = "`BossPhoneMob`", nullable = false)
    @Length(max = 100, message = "Длина поля Мобильный телефон руководителя не должна превышать 100 символов")
    private String bossPhoneMob = "";

    @Column(name = "`BossPhoneWork`", nullable = false)
    @Length(max = 100, message = "Длина поля Рабочий телефон руководителя не должна превышать 100 символов")
    private String bossPhoneWork = "";

    @Column(name = "`BossEmail`", nullable = false)
    @Length(max = 50, message = "Длина поля EMail руководителя не должна превышать 50 символов")
    private String bossEmail = "";

    //
    //  Бухгалтер
    //

    @Column(name = "`BuhFio`", nullable = false)
    @Length(max = 122, message = "Длина поля ФИО бухгалтера не должна превышать 122 символов")
    private String buhFio = "";

    @Column(name = "`BuhPhoneMob`", nullable = false)
    @Length(max = 100, message = "Длина поля Мобильный телефон бухгалтера не должна превышать 100 символов")
    private String buhPhoneMob = "";

    @Column(name = "`BuhPhoneWork`", nullable = false)
    @Length(max = 100, message = "Длина поля Рабочий телефон бухгалтера не должна превышать 100 символов")
    private String buhPhoneWork = "";

    @Column(name = "`BuhEmail`", nullable = false)
    @Length(max = 50, message = "Длина поля EMail бухгалтера не должна превышать 50 символов")
    private String buhEmail = "";


    //
    //  Банковские реквезиты
    //

    /**
     * Расчетный счет
     */
    @Length(max = 20, message = "Длина поля Р/Счет не должна превышать 20 символов")
    @Column(name = "`Account`", nullable = false)
    private String account = "";

    @Length(max = 12, message = "Длина поля ИНН не должна превышать 12 символов")
    @Column(name = "`Inn`", nullable = false)
    private String inn = "";

    @Length(max = 9, message = "Длина поля ПКК не должна превышать 9 символов")
    @Column(name = "`Kpp`", nullable = false)
    private String kpp = "";

    @Length(max = 9, message = "Длина поля БИК не должна превышать 9 символов")
    @Column(name = "`Bik`", nullable = false)
    private String bik = "";

    @Length(max = 120, message = "Длина поля Банк не должна превышать 120 символов")
    @Column(name = "`Bank`", nullable = false)
    private String bank = "";

    @Length(max = 30, message = "Длина коля К/Счет не должна превышать 30 символов")
    @Column(name = "`AccountCor`", nullable = false)
    private String accountCor = "";

    @Length(max = 30, message = "Длина поля ОКПО не должна превышать 30 символов")
    @Column(name = "`Okpo`", nullable = false)
    private String okpo = "";

    @Length(max = 30, message = "Длина поля ОКОНХ не должна превышать 30 символов")
    @Column(name = "`Okonx`", nullable = false)
    private String okonh = "";

    @Length(max = 30, message = "Длина поля не должна превышать 30 символов")
    @Column(name = "`Ogrn`", nullable = false)
    private String ogrn = "";


    @Column(name = "`Comment`", nullable = false)
    @Length(max = 500, message = "Длина поля Комментарий не должна превышать 500 символов")
    private String comment = "";


    @Column(name = "`MaxPrice`", nullable = false)
    private float maxPrice = 0;

    @Column(name = "`MinSum`", nullable = false)
    private float minSum = 0;


    @Column(name = "`CreationDate`", nullable = false)
    private Timestamp creationDate = GoodfilUtils.getCurrentTimestamp();

    //
    //  section Долг по банку, долг по кассе
    //

    @Column(name = "`DebtBank`")
    private float debtBank;

    @Column(name = "`DebtCash`")
    private float debtCash;


    //
    //  section Параметры доставки
    //

    @Column(name = "`IdDeliveryType`", nullable = false)
    private int deliveryTypeId = DeliveryType.SELF_DELIVERY;

    //
    //  section Персональная информация и регистрационный код
    //

    /**
     * Регистрационный код клиента
     */
    @Column(name = "`RegistrationCode`", length = 50, nullable = false)
    private String registrationCode = "";

    /**
     * Персональная информация для клиента
     */
    @Column(name = "`InfoText`", length = 500, nullable = false)
    private String infoText = "";

    @Column(name = "ClientStateId", nullable = false)
    private int clientStateId = -1;

    @Column(name = "IsMain", nullable = false)
    private boolean isMain = false;

    @Column(name = "IsSendInvoice", nullable = false)
    private boolean isSendInvoice;

    @Column(name = "Republic", nullable = false)
    private String republic = "";

    @Column(name = "Region", nullable = false)
    private String region = "";

    @Column(name = "Kray", nullable = false)
    private String kray = "";

    @Column(name = "City", nullable = false)
    private String city = "";

    @Column(name = "Street", nullable = false)
    private String street = "";

    @Column(name = "House", nullable = false)
    private String house = "";

    /**
     * Флаг, указывающий заблокирован ли клиент в 1С
     */
    @Column(name = "C1Block")
    private boolean c1Block = false;

    /**
     * Простой механизм разблокировки. Если да, то смотрим блокировку 1С. Если нет, то не смотрим и даем 1С команду, чтобы
     * она тоже не смотрела
     */
    @Column(name = "IsBlockCheck", nullable = false)
    private boolean isBlockCheck = true;

    @Transient
    private User user;

    @Transient
    private User manager;

//    @Transient
//    private ClientGroup clientGroup;

    @Transient
    private int ordersCount = 0;

    public boolean isC1Block() {
        return c1Block;
    }

    public void setC1Block(boolean c1Block) {
        this.c1Block = c1Block;
    }

    public String getRepublic() {
        return republic;
    }

    public void setRepublic(String republic) {
        this.republic = republic;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public String getKray() {
        return kray;
    }

    public void setKray(String kray) {
        this.kray = kray;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public boolean isSendInvoice() {
        return isSendInvoice;
    }

    public void setSendInvoice(boolean isSendInvoice) {
        this.isSendInvoice = isSendInvoice;
    }

    public int getClientStateId() {
        return clientStateId;
    }

    public void setClientStateId(int clientStateId) {
        this.clientStateId = clientStateId;
    }

    public String getClientStateAsString(){
        return ClientState.asString(clientStateId);
    }

    /**
     * Перечень видимых для клиента клиентских групп
     */
    @Transient
    private Set<Long/*clientGroupId*/> visibleClientGroups;

    public void initVisibleClientGroups() {
        visibleClientGroups = new HashSet<Long>();
    }

    public Set<Long/*clientGroupId*/> getVisibleClientGroups() {
        return visibleClientGroups;
    }

    public void setVisibleClientGroups(Set<Long> visibleClientGroups) {
        this.visibleClientGroups = visibleClientGroups;
    }

    /**
     * Перечень видимых для клиента брэндов
     */
    @Transient
    private Set<Long> visibleBrands;

    public void initVisibleBrands() {
        visibleBrands = new HashSet<Long>();
    }

    public Set<Long> getVisibleBrands() {
        return visibleBrands;
    }

    public void setVisibleBrands(Set<Long> visibleBrands) {
        this.visibleBrands = visibleBrands;
    }

    /**
     * Перечень клиентских групп, к которым относится клиент
     */
    @Transient
    private final Map<Long/*brandId*/, Long/*clientGroupdId*/> clientgroups = new HashMap<Long, Long>();

    /**
     * Перечень клиентских групп, к которым относится клиент (по брэндам)
     */
    public Map<Long/*brandId*/, Long/*clientGroupdId*/> getClientgroups() {
        return clientgroups;
    }

    /**
     * Получить клиентскую группу клиента по заданному идентификатору брэнда
     *
     * @param brandId идентификатор брэнда
     * @return идентификатор клиентской группы клиента по заданному идентификатору брэнда
     */
    public Long getClientgroupIdByBrandId(long brandId) {
        return clientgroups.get(brandId);
    }

    /**
     * Расширенная информация о доставке
     */
    @Transient
    private ClientDeliveryInfoExt deliveryInfoExt = new ClientDeliveryInfoExt();

    public ClientDeliveryInfoExt getDeliveryInfoExt() {
        return deliveryInfoExt;
    }

    public void setDeliveryInfoExt(ClientDeliveryInfoExt deliveryInfoExt) {
        this.deliveryInfoExt = deliveryInfoExt;
    }

    //
    //  Clever properties
    //

    public Timestamp getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Timestamp creationDate) {
        this.creationDate = creationDate;
    }

    public User getManager() {
        return manager;
    }

    public void setManager(User manager) {
        assert manager != null;
        assert managerId == manager.getId();

        this.manager = manager;
    }

    //    public ClientGroup getClientGroup()
//    {
//        return clientGroup;
//    }
//
//    public void setClientGroup(ClientGroup clientGroup)
//    {
//        assert clientGroup != null;
//        assert clientGroupId == clientGroup.getId();
//
//        this.clientGroup = clientGroup;
//    }
//
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        assert user != null;

        this.user = user;
    }

    public int getOrdersCount() {
        return ordersCount;
    }

    public void setOrdersCount(int ordersCount) {
        this.ordersCount = ordersCount;
    }


    //
    //  Stupid properties
    //

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

//    public long getClientGroupId()
//    {
//        return clientGroupId;
//    }
//
//    public void setClientGroupId(long clientGroupId)
//    {
//        this.clientGroupId = clientGroupId;
//    }

    public Long getManagerId() {
        return managerId;
    }

    public void setManagerId(Long managerId) {
        this.managerId = managerId;
    }

    public String getCompanyName() {
        return companyName;
    }

    public void setCompanyName(String companyName) {
        this.companyName = s(companyName);
    }

    public String getLowName() {
        return lowName;
    }

    public void setLowName(String lowName) {
        this.lowName = s(lowName);
    }

    public String getCompanyType() {
        return companyType;
    }

    public void setCompanyType(String companyType) {
        this.companyType = s(companyType);
    }

    public String getSpeciality() {
        return speciality;
    }

    public void setSpeciality(String speciality) {
        this.speciality = s(speciality);
    }

    public String getSite() {
        return site;
    }

    public void setSite(String site) {
        this.site = s(site);
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public boolean isAllowControlSum() {
        return allowControlSum;
    }

    public void setAllowControlSum(boolean allowControlSum) {
        this.allowControlSum = allowControlSum;
    }

    public boolean isAllowQuickOrder() {
        return allowQuickOrder;
    }

    public void setAllowQuickOrder(boolean allowQuickOrder) {
        this.allowQuickOrder = allowQuickOrder;
    }

    public String getC1ClientNum() {
        return c1ClientNum;
    }

    public void setC1ClientNum(String c1ClientNum) {
        this.c1ClientNum = s(c1ClientNum);
    }

    public String getAddressFact() {
        return addressFact;
    }

    public String getAddressFactTrimmed() {
        if (isBlank(addressFact)) {
            return "";
        }

        int len = 30;
        if (addressFact.length() < len) {
            return addressFact;
        } else {
            return addressFact.substring(0, len - 1) + " [...]";
        }
    }

    public void setAddressFact(String addressFact) {
        this.addressFact = s(addressFact);
    }

    public String getAddressLaw() {
        return addressLaw;
    }

    public String getAddressLawTrimmed() {
        if (isBlank(addressLaw)) {
            return "";
        }

        int len = 30;
        if (addressLaw.length() < len) {
            return addressLaw;
        } else {
            return addressLaw.substring(0, len - 1) + " [...]";
        }
    }

    public void setAddressLaw(String addressLaw) {
        this.addressLaw = s(addressLaw);
    }

    public String getAddressDlv() {
        return addressDlv;
    }

    public String getAddressDlvTrimmed() {
        if (isBlank(addressDlv)) {
            return "";
        }

        int len = 30;
        if (addressDlv.length() < len) {
            return addressDlv;
        } else {
            return addressDlv.substring(0, len - 1) + " [...]";
        }
    }

    public void setAddressDlv(String addressDlv) {
        this.addressDlv = addressDlv == null ? "" : addressDlv.trim();
    }

    public String getContactFio() {
        return contactFio;
    }

    public void setContactFio(String contactFio) {
        this.contactFio = s(contactFio);
    }

    public String getContactPhoneMob() {
        return contactPhoneMob;
    }

    public void setContactPhoneMob(String contactPhoneMob) {
        this.contactPhoneMob = s(contactPhoneMob);
    }

    public String getContactPhoneWork() {
        return contactPhoneWork;
    }

    public void setContactPhoneWork(String contactPhoneWork) {
        this.contactPhoneWork = s(contactPhoneWork);
    }

    public String getContactEmail() {
        return contactEmail;
    }

    public void setContactEmail(String contactEmail) {
        this.contactEmail = s(contactEmail);
    }

    public String getBossFio() {
        return bossFio;
    }

    public void setBossFio(String bossFio) {
        this.bossFio = s(bossFio);
    }

    public String getBossPhoneMob() {
        return bossPhoneMob;
    }

    public void setBossPhoneMob(String bossPhoneMob) {
        this.bossPhoneMob = s(bossPhoneMob);
    }

    public String getBossPhoneWork() {
        return bossPhoneWork;
    }

    public void setBossPhoneWork(String bossPhoneWork) {
        this.bossPhoneWork = s(bossPhoneWork);
    }

    public String getBossEmail() {
        return bossEmail;
    }

    public void setBossEmail(String bossEmail) {
        this.bossEmail = s(bossEmail);
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = s(account);
    }

    public String getInn() {
        return inn;
    }

    public void setInn(String inn) {
        this.inn = s(inn);
    }

    public String getKpp() {
        return kpp;
    }

    public void setKpp(String kpp) {
        this.kpp = s(kpp);
    }

    public String getBik() {
        return bik;
    }

    public void setBik(String bik) {
        this.bik = s(bik);
    }

    public String getBank() {
        return bank;
    }

    public void setBank(String bank) {
        this.bank = s(bank);
    }

    public String getAccountCor() {
        return accountCor;
    }

    public void setAccountCor(String accountCor) {
        this.accountCor = s(accountCor);
    }

    public String getOkpo() {
        return okpo;
    }

    public void setOkpo(String okpo) {
        this.okpo = s(okpo);
    }

    public String getOkonh() {
        return okonh;
    }

    public void setOkonh(String okonh) {
        this.okonh = s(okonh);
    }

    public String getOgrn() {
        return ogrn;
    }

    public void setOgrn(String ogrn) {
        this.ogrn = s(ogrn);
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = s(comment);
    }

    public float getMaxPrice() {
        return maxPrice;
    }

    public void setMaxPrice(float maxPrice) {
        this.maxPrice = maxPrice;
    }

    public float getMinSum() {
        return minSum;
    }

    public void setMinSum(float minSum) {
        this.minSum = minSum;
    }

    public String getBuhFio() {
        return buhFio;
    }

    public void setBuhFio(String buhFio) {
        this.buhFio = s(buhFio);
    }

    public String getBuhPhoneMob() {
        return buhPhoneMob;
    }

    public void setBuhPhoneMob(String buhPhoneMob) {
        this.buhPhoneMob = s(buhPhoneMob);
    }

    public String getBuhPhoneWork() {
        return buhPhoneWork;
    }

    public void setBuhPhoneWork(String buhPhoneWork) {
        this.buhPhoneWork = s(buhPhoneWork);
    }

    public String getBuhEmail() {
        return buhEmail;
    }

    public void setBuhEmail(String buhEmail) {
        this.buhEmail = s(buhEmail);
    }

    public float getDebtBank() {
        return debtBank;
    }

    public void setDebtBank(float debtBank) {
        this.debtBank = debtBank;
    }

    public float getDebtCash() {
        return debtCash;
    }

    public void setDebtCash(float debtCash) {
        this.debtCash = debtCash;
    }

    public int getDeliveryTypeId() {
        return deliveryTypeId;
    }

    public void setDeliveryTypeId(int deliveryTypeId) {
        this.deliveryTypeId = deliveryTypeId;
    }

    public String getRegistrationCode() {
        return registrationCode;
    }

    public void setRegistrationCode(String registrationCode) {
        this.registrationCode = s(registrationCode);
    }

    public String getInfoText() {
        return infoText;
    }

    public void setInfoText(String infoText) {
        this.infoText = s(infoText);
    }

    /**
     * Возвращает true, если у клиента отсутствует код соответствия с 1С
     *
     * @return true, если у клиента отсутствует код соответствия с 1С
     */
    public boolean getC1CodeIsMissing() {
        return StringUtils.isBlank(c1ClientNum);
    }

    /**
     * Возвращает true, если у клиента отсутствует код соответствия с 1С
     *
     * @return true, если у клиента отсутствует код соответствия с 1С
     */
    public boolean getManagerIsMissing() {
        return managerId == null;
    }

    /**
     * Представить клиента в виде строки например "Клиент "ООО Копыта""
     *
     * @return
     */
    public String toClientString() {
        return "Клиент \"" + companyName + "\"";
    }

    public boolean isClientSateIsTD(){
        return getClientStateId() == ClientState.TD;
    }

    public boolean isClientSateIsESHOPDAYTODAY(){
        return getClientStateId() == ClientState.E_SHOP_DAY_TO_DAY;
    }

    public boolean isClientStateIsEXPRESSDELIVERY(){
        return getClientStateId() == ClientState.EXPRESS_DELIVERY;
    }

    public boolean isMain() {
        return isMain;
    }

    public void setMain(boolean isMain) {
        this.isMain = isMain;
    }

    public String getClientWorkStateAsString(){
        return isActive() ? "Активный" : "Архив";
    }

    public String getClientIsBlockAsString(){
        return getClientIsBlock() ?"Блок" :"--";
    }

    public String getHouse() {
        return house;
    }

    public void setHouse(String house) {
        this.house = house;
    }

    public boolean isBlockCheck() {
        return isBlockCheck;
    }

    public void setBlockCheck(boolean isBlockCheck) {
        this.isBlockCheck = isBlockCheck;
    }

    public boolean getClientIsBlock(){
        return isBlockCheck && c1Block;
    }
}
